package org.notabug.lifeuser.moviedb;

import android.content.Intent;
import android.content.Context;
import android.content.SharedPreferences;

import android.os.Bundle;
import android.os.AsyncTask;
import android.util.Log;
import android.view.View;

import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ListView;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;

import java.lang.StringBuilder;

/*
* This class provides an overview of the popular movies/series.
* This class contains a listview that presents all the movies/series.
*/
public class MovieActivity extends BaseActivity implements AbsListView.OnScrollListener {

	private MovieBaseAdapter movieAdapter;
	private MovieBaseAdapter searchMovieAdapter;
	private ArrayList<JSONObject> movieArrayList;
	private ArrayList<JSONObject> searchMovieArrayList;
	private HashMap<String, String> movieGenreList;
	private ListView listView;
	private AsyncTask searchTask;
	private String listType;
	private boolean searchView;
	private String searchQuery;

	// Variables for scroll detection
	private int visibleThreshold = 5;
	private int currentPage = 0;
	private int currentSearchPage = 0;
	private int previousTotal = 0;
	private boolean loading = true;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_movie);
		setNavigationDrawer();
		
		// Get intent if user choose from the navigationView,
		// if no intent is found, assume movie.
		try {
			listType = getIntent().getExtras().getString("movies");
			if(listType != null) {
				createMovieList(listType);
			} else {
				listType = "movie";
				createMovieList("movie");
			}
		} catch(NullPointerException npe) {
			listType = "movie";
			createMovieList("movie");
		}

		if(listType.equals("tv")) {
			setTitle(getString(R.string.title_activity_movie_series));
		} else {
			setTitle(getString(R.string.title_activity_movie_movie));
		}
	}
	
	/**
	* Creates and visualizes a list of movies or series on the screen.
	*
	* @param mode determines if series or movies will be retrieved, movies is "movie".
	*/
	private void createMovieList(String mode) {
		listView = (ListView) findViewById(R.id.listViewData);

		// Create a movieBaseAdapter and load the first page
		movieArrayList = new ArrayList<JSONObject>();
		movieGenreList = new HashMap<String, String>();
		movieAdapter = new MovieBaseAdapter(movieArrayList,
				movieGenreList, getApplicationContext());

		new GenreList().execute(mode);
		new MovieList().execute(mode, "1");


		// Send user (and movieObject) to DetailActivity.
		listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view,
									int position, long id) {
				Intent intent = new Intent(getApplicationContext(),
						DetailActivity.class);

				// Select the right list to get the JSONObject from.
				JSONObject movieObject;
				if(searchView) {
					movieObject = (JSONObject) searchMovieAdapter.getItem(position);
				} else {
					movieObject = (JSONObject) movieAdapter.getItem(position);
				}
				
				if(listType.equals("tv")) {
					intent.putExtra("isMovie", false);
				}

				// Extra's can't be any object so JSONObject needs to be converted.
				intent.putExtra("movieObject", movieObject.toString());
				startActivity(intent);
			}
		});

		listView.setOnScrollListener(this);
	}

	@Override
	public void doSearch() {
		// Get the query
		searchQuery = editSearch.getText().toString();

		// Create movieBaseAdapter and load the search results
		searchMovieArrayList = new ArrayList<JSONObject>();
		searchMovieAdapter = new MovieBaseAdapter(searchMovieArrayList,
				movieGenreList, getApplicationContext());

		// Cancel old AsyncTask if exists
		currentSearchPage = 1;
		if(searchTask != null) {
			searchTask.cancel(true);
			searchTask = new SearchList().execute(listType, "1", searchQuery);
		} else {
			searchTask = new SearchList().execute(listType, "1", searchQuery);
		}
	}

	@Override
	public void searchCancelled() {
		// Replace the current list with the movieList.
		searchView = false;
		listView.setAdapter(movieAdapter);
	}

	@Override
	public void onScroll(AbsListView view, int firstVisibleItem,
						 int visibleItemCount, int totalItemCount) {
		if(loading) {
			if(totalItemCount > previousTotal) {
				// If the new movies/series are added to the list, 
				// there is a new "page" and the loading is stopped.
				loading = false;
				previousTotal = totalItemCount;

				if(searchView) {
					currentSearchPage++;
				} else {
					currentPage++;
				}
			}
		}

		// When no new pages are being loaded,
		// but the user is at the end of the list, load the new page.
		if(!loading && (totalItemCount - visibleItemCount) <=
				(firstVisibleItem + visibleThreshold)) {
			// Load the next page of content in the background.
			if(searchView) {
				searchTask = new SearchList().execute(listType,
						Integer.toString(currentSearchPage + 1), searchQuery);
			} else {
				new MovieList().execute(listType, Integer.toString(currentPage + 1));
			} 
			loading = true;
		}
	}

	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {}

	// Load the list with popular movies/series.
	private class MovieList extends AsyncTask<String, Void, String> {

		private final String API_KEY = ConfigHelper.getConfigValue(
				getApplicationContext(), "api_key");
		private boolean missingOverview;
		private String listType;
		private int page;
		
		protected String doInBackground(String... params) {
			listType = params[0];
			page = Integer.parseInt(params[1]);
			if(params.length > 2) {
				missingOverview = params[2].equalsIgnoreCase("true");
			}

			String line;
			StringBuilder stringBuilder = new StringBuilder();

			// Load the webpage with the popular movies/series.
			try {
				URL url;
				if(missingOverview) {
					url = new URL("https://api.themoviedb.org/3/discover/"
						+ listType + "?sort_by=popularity.desc&page="
						+ page + "&api_key=" + API_KEY);
				} else {
					url = new URL("https://api.themoviedb.org/3/discover/"
						+ listType + "?sort_by=popularity.desc&page="
						+ page + "&api_key=" + API_KEY + getLanguageParameter());
				}

				URLConnection urlConnection = url.openConnection();
				try {
					BufferedReader bufferedReader = new BufferedReader(
							new InputStreamReader(
							urlConnection.getInputStream()));

					// Create one long string of the webpage.
					while((line = bufferedReader.readLine()) != null) {
						stringBuilder.append(line).append("\n");
					}

					// Close connection and return the data from the webpage.
					bufferedReader.close();
					return stringBuilder.toString();
				} catch(IOException ioe) {
					ioe.printStackTrace();
				}
			} catch(MalformedURLException mue) {
				mue.printStackTrace();
			} catch(IOException ioe) {
				ioe.printStackTrace();
			}

			// Loading the dataset failed, return null.
			return null;
		}

		protected void onPostExecute(String response) {
			// Keep the user at the same position in the list.
			int position;
			try {
				position = listView.getFirstVisiblePosition();
			} catch(NullPointerException npe) {
				position = 0;
			}
			
			// Convert the JSON data from the webpage into JSONObjects
			ArrayList<JSONObject> tempMovieArrayList = new ArrayList<JSONObject>();
			try {	
				JSONObject reader = new JSONObject(response);
				JSONArray arrayData = reader.getJSONArray("results");
				for(int i = 0; i < arrayData.length(); i++) {
					JSONObject websiteData = arrayData.getJSONObject(i);
					if(missingOverview) {
						tempMovieArrayList.add(websiteData);
					} else {
						movieArrayList.add(websiteData);
					}
				}

				// Some translations might be lacking and need to be filled in.
				// Therefore, load the same list but in English.
				// After that, iterate through the translated list
				// and fill in any missing parts.
				if(!Locale.getDefault().getLanguage().equals("en") &&
							!missingOverview) {
					new MovieList().execute(listType, Integer.toString(page), "true");
				}

				if(missingOverview) {
					for(int i = 0; i < movieArrayList.size(); i++) {
						JSONObject movieObject = movieArrayList.get(i);
						if(movieObject.getString("overview").equals("")) {
							movieObject.put("overview", tempMovieArrayList.
									get(i).getString("overview"));
						}
					}
				}

				// Reload the adapter (with the new page) 
				// and set the user to his old position.
				listView.setAdapter(movieAdapter);
				listView.setSelection(position);
			} catch(JSONException je) {
				je.printStackTrace();
			}
		}
	}


	// Load the genres
	private class GenreList extends AsyncTask<String, Void, String> {

		private final String API_KEY = ConfigHelper.getConfigValue(
				getApplicationContext(), "api_key");
		
		protected String doInBackground(String... params) {
			String genreType = params[0];

			String line;
			StringBuilder stringBuilder = new StringBuilder();

			// Load the genre webpage.
			try {
				URL url = new URL("https://api.themoviedb.org/3/genre/"
						+ genreType + "/list?api_key=" + 
						API_KEY + getLanguageParameter());

				URLConnection urlConnection = url.openConnection();
				try {
					BufferedReader bufferedReader = new BufferedReader(
							new InputStreamReader(
							urlConnection.getInputStream()));

					// Create one long string of the webpage.
					while((line = bufferedReader.readLine()) != null) {
						stringBuilder.append(line).append("\n");
					}

					// Close connection and return the data from the webpage.
					bufferedReader.close();
					return stringBuilder.toString();
				} catch(IOException ioe) {
					ioe.printStackTrace();
				}
			} catch(MalformedURLException mue) {
				mue.printStackTrace();
			} catch(IOException ioe) {
				ioe.printStackTrace();
			}

			// Loading the dataset failed, return null.
			return null;
		}

		protected void onPostExecute(String response) {
			// Keep the user at the same position in the list.
			int position;
			try {
				position = listView.getFirstVisiblePosition();
			} catch(NullPointerException npe) {
				position = 0;
			}

			// Save GenreList to sharedPreferences, this way it can be used anywhere.
			SharedPreferences sharedPreferences = getApplicationContext()
					.getSharedPreferences("GenreList", Context.MODE_PRIVATE);
			SharedPreferences.Editor prefsEditor = 
					sharedPreferences.edit();

			// Convert the JSON data from the webpage to JSONObjects in an ArrayList.
			try {	
				JSONObject reader = new JSONObject(response);
				JSONArray genreArray = reader.getJSONArray("genres");
				for(int i = 0; genreArray.getJSONObject(i) != null; i++) {
					JSONObject websiteData = genreArray.getJSONObject(i);
					movieGenreList.put(websiteData.getString("id"),
							websiteData.getString("name"));

				// Temporary fix until I find a way to handle this efficiently.
				prefsEditor.putString(websiteData.getString("id"),
						websiteData.getString("name"));
				prefsEditor.commit();
				}

				movieAdapter.notifyDataSetChanged();
			} catch(JSONException je) {
				je.printStackTrace();
			}
		}
	}

	// Load a list of movies/series that fulfull the search query.
	private class SearchList extends AsyncTask<String, Void, String> {

		private final String API_KEY = ConfigHelper.getConfigValue(
				getApplicationContext(), "api_key");
		private boolean missingOverview;
		private int page;

		protected String doInBackground(String... params) {
			// Extra parameter for the search query.
			String listType = params[0];
			page = Integer.parseInt(params[1]);
			String query = params[2];
			if(params.length > 3) {
				missingOverview = params[3].equalsIgnoreCase("true");
			}

			String line;
			StringBuilder stringBuilder = new StringBuilder();

			// Load the webpage with the list of movies/series.
			try {
				URL url;
				if(missingOverview) {
					url = new URL("https://api.themoviedb.org/3/search/" + 
							listType + "?query=" + query + "&page=" + page + 
							"&api_key=" + API_KEY);
				} else {
					url = new URL("https://api.themoviedb.org/3/search/" + 
							listType + "?query=" + query + "&page=" + page + 
							"&api_key=" + API_KEY + getLanguageParameter());
				}

				URLConnection urlConnection = url.openConnection();
				try {
					BufferedReader bufferedReader = new BufferedReader(
							new InputStreamReader(
							urlConnection.getInputStream()));

					// If the user changed the search query, stop loading.
					if(isCancelled()) {
						return null;
					}

					// Create one long string of the webpage.
					while((line = bufferedReader.readLine()) != null) {
						// If the user changed the search query, stop loading.
						if(isCancelled()) {
							// There are two because I am not sure
							// what the best place is for this clause.
							break;
						}
						stringBuilder.append(line).append("\n");
					}

					// Close connection and return the data from the webpage.
					bufferedReader.close();
					return stringBuilder.toString();
				} catch(IOException ioe) {
					ioe.printStackTrace();
				}
			} catch(MalformedURLException mue) {
				mue.printStackTrace();
			} catch(IOException ioe) {
				ioe.printStackTrace();
			}

			// Loading the dataset failed, return null.
			return null;
		}

		protected void onPostExecute(String response) {
			// Keep the user at the same position in the list.
			int position;
			try {
				position = listView.getFirstVisiblePosition();
			} catch(NullPointerException npe) {
				position = 0;
			}
			
			// Clear the array list before adding new movies to it.
			if(currentSearchPage <= 0) {
				// Only if the list doesn't already contain search results.
				searchMovieArrayList.clear();
			}

			// Convert the JSON webpage to JSONObjects
			// Add the JSONObjects to the list with movies/series.
			if(!(response == null || response.equals(""))) {
				ArrayList<JSONObject> tempSearchMovieArrayList = new ArrayList<JSONObject>();
				try {	
					JSONObject reader = new JSONObject(response);
					JSONArray arrayData = reader.getJSONArray("results");
					for(int i = 0; i < arrayData.length(); i++) {
						JSONObject websiteData = arrayData.getJSONObject(i);
						if(missingOverview) {
							tempSearchMovieArrayList.add(websiteData);
						} else {
							searchMovieArrayList.add(websiteData);
						}
					}

					// Some translations might be lacking and need to be filled in.
					// Therefore, load the same list but in English.
					// After that, iterate through the translated list
					// and fill in any missing parts.
					if(!Locale.getDefault().getLanguage().equals("en") && !missingOverview) {
						searchTask = new SearchList().execute(listType,
								Integer.toString(page), searchQuery, "true");
					}

					if(missingOverview) {
						for(int i = 0; i < searchMovieArrayList.size(); i++) {
							JSONObject movieObject = searchMovieArrayList.get(i);
							if(movieObject.getString("overview").equals("")) {
								movieObject.put("overview", tempSearchMovieArrayList.
										get(i).getString("overview"));
							}
						}
					}

					// Reload the adapter (with the new page) 
					// and set the user to his old position.
					searchView = true;
					listView.setAdapter(searchMovieAdapter);
					listView.setSelection(position);
				} catch(JSONException je) {
					je.printStackTrace();
				}
			}
		}
	}
}
